package com.wiscamp.ninechapters.accounts.domain.repositories;

import com.wiscamp.ninechapters.accounts.domain.models.*;
import com.wiscamp.ninechapters.accounts.infrastructure.mappers.AccountBatchLogMapper;
import com.wiscamp.ninechapters.accounts.infrastructure.mappers.AccountBatchMapper;
import com.wiscamp.ninechapters.accounts.infrastructure.mappers.AccountLogMapper;
import com.wiscamp.ninechapters.accounts.infrastructure.mappers.AccountMapper;
import com.wiscamp.ninechapters.accounts.infrastructure.po.*;
import cube.ddd.aspect.AopDataException;
import com.wiscamp.ninechapters.common.core.CollectionHelper;
import com.wiscamp.ninechapters.common.persistence.DbHelper;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Account Repository
 */
@Repository
public class AccountsRepository {
    private Logger logger = LoggerFactory.getLogger(this.getClass());


    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountBatchMapper accountBatchMapper;

    @Autowired
    private AccountLogMapper accountLogMapper;

    @Autowired
    private AccountBatchLogMapper accountBatchLogMapper;

    @Autowired
    public AccountsRepository(AccountMapper accountMapper, AccountBatchMapper accountBatchMapper, AccountLogMapper accountLogMapper, AccountBatchLogMapper accountBatchLogMapper) {
        this.accountMapper = accountMapper;
        this.accountBatchMapper = accountBatchMapper;
        this.accountLogMapper = accountLogMapper;
        this.accountBatchLogMapper = accountBatchLogMapper;
    }

    //region converter

    private Account toAccount(@NotNull AccountPO accountPO) {
        var account = new Account();
        BeanUtils.copyProperties(accountPO, account);
        if (account.isBatchManagement()) {
            var batches = CollectionHelper.convertToArrayList(accountPO.getAccountBatches(), this::toAccountBatch);
            account.setAccountBatches(batches);
        }
        return account;
    }

    private AccountPO toAccountPO(@NotNull Account account) {
        var accountPO = new AccountPO();
        BeanUtils.copyProperties(account, accountPO);
        if (account.isBatchManagement()) {
            var poBatches = CollectionHelper.convertToArrayList(account.getAccountBatches(), this::toAccountBatchPO);
            accountPO.setAccountBatches(poBatches);
        }
        return accountPO;
    }


    private AccountBatch toAccountBatch(@NotNull AccountBatchPO po) {
        var entity = new AccountBatch();
        BeanUtils.copyProperties(po, entity);
        return entity;
    }

    private AccountBatchPO toAccountBatchPO(@NotNull AccountBatch entity) {
        var po = new AccountBatchPO();
        BeanUtils.copyProperties(entity, po);
        return po;
    }

    private AccountLog toAccountLog(@NotNull AccountLogPO po) {
        var entity = new AccountLog();
        BeanUtils.copyProperties(po, entity);
        if (po.isBatchManagement()) {
            var batches = CollectionHelper.convertToArrayList(po.getAccountLogBatches(), this::toAccountLogBatch);
            entity.setAccountBatchLogs(batches);
        }
        return entity;
    }

    private AccountLogPO toAccountLogPO(@NotNull AccountLog entity) {
        var po = new AccountLogPO();
        BeanUtils.copyProperties(entity, po);
        if (po.isBatchManagement()) {
            var batches = CollectionHelper.convertToArrayList(entity.getAccountBatchLogs(), this::toAccountLogBatchPO);
            po.setAccountLogBatches(batches);
        }
        return po;
    }

    private AccountBatchSnapshot toAccountBatchSnapshot(@NotNull AccountBatchSnapshotPO po) {
        var entity = new AccountBatchSnapshot();
        BeanUtils.copyProperties(po, entity);
        return entity;
    }

    private AccountBatchSnapshotPO toAccountBatchSnapshotPO(@NotNull AccountBatchSnapshot entity) {
        var po = new AccountBatchSnapshotPO();
        BeanUtils.copyProperties(entity, po);
        return po;
    }

    private AccountBatchLog toAccountLogBatch(@NotNull AccountBatchLogPO po) {
        var entity = new AccountBatchLog();
        BeanUtils.copyProperties(po, entity);
        return entity;
    }

    private AccountBatchLogPO toAccountLogBatchPO(@NotNull AccountBatchLog entity) {
        var po = new AccountBatchLogPO();
        BeanUtils.copyProperties(entity, po);
        return po;
    }

    private AccountSnapshot toAccountSnapshot(@NotNull AccountSnapshotPO po) {
        var entity = new AccountSnapshot();
        BeanUtils.copyProperties(po, entity);
        return entity;
    }

    private AccountSnapshotPO toAccountSnapshotPO(@NotNull AccountSnapshot entity) {
        var po = new AccountSnapshotPO();
        BeanUtils.copyProperties(entity, po);
        return po;
    }

    @AopDataException
    public Account getAccountByType(int accountType, @Min(0) long userId) {
        var accountPO = accountMapper.getAccount(accountType, userId);
        if (Objects.isNull(accountPO)) return null;

        var account = toAccount(accountPO);
        if (account.isBatchManagement()) {
            account.setAccountBatches(getAccountBatches(accountPO.getAccountId(), accountType, userId));
        }
        return account;
    }
    //endregion


    /**
     * Gets a account' batches
     *
     * @param accountId
     * @param accountType
     * @param userId
     * @return
     */
    @AopDataException
    public Collection<AccountBatch> getAccountBatches(@Min(0) long accountId, @Min(0) int accountType, @Min(0) long userId) {
        var poList = accountBatchMapper.getAccountBatches(accountId, accountType, userId);
        return CollectionHelper.convertToArrayList(poList, this::toAccountBatch);
    }

    /**
     * Gets account logs
     *
     * @param accountType
     * @param userId
     * @return
     */
    @AopDataException
    public Collection<AccountLog> getAccountLogs(@Min(0) int accountType, @Min(0) long userId) {
        var accountPO = accountMapper.getAccount(accountType, userId);
        Collection<AccountLogPO> logs = accountLogMapper.getAccountLogs(accountType, userId);
        if (accountPO.isBatchManagement()) {
            Collection<AccountBatchLogPO> poLogBatches = accountBatchLogMapper.getAccountBatchLogs(accountType, userId);
            for (AccountLogPO log : logs) {
                Collection<AccountBatchLogPO> temp = poLogBatches.stream().filter(b -> b.getLogId() == log.getLogId()).collect(Collectors.toList());
                log.setAccountLogBatches(temp);
            }
        }
        return CollectionHelper.convertToArrayList(logs, this::toAccountLog);
    }

    @AopDataException
    public Account saveAccount(@NotNull Account account) {
        var accountPO = toAccountPO(account);
        var savedPO = DbHelper.save(accountPO, accountMapper::saveAndFlush);
        return toAccount(savedPO);
    }

    @AopDataException
    public AccountBatch saveAccountBatch(@NotNull AccountBatch accountBatch) {
        var accountBatchPO = toAccountBatchPO(accountBatch);
        var savedPO = DbHelper.save(accountBatchPO, accountBatchMapper::saveAndFlush);
        return toAccountBatch(savedPO);
    }

    @AopDataException
    public AccountLog saveAccountLog(@NotNull AccountLog accountLog) {
        var po = toAccountLogPO(accountLog);
        var savedPO = DbHelper.save(po, accountLogMapper::saveAndFlush);
        return toAccountLog(savedPO);
    }

    @AopDataException
    public AccountBatchLog saveAccountLogBatch(@NotNull AccountBatchLog accountBatchLog) {
        var po = toAccountLogBatchPO(accountBatchLog);
        var savedPO = DbHelper.save(po, accountBatchLogMapper::saveAndFlush);
        return toAccountLogBatch(savedPO);
    }
}
